﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Text;

namespace AjaxUploadServer
{
	public class ServerManage
	{
		/// <summary>
		/// 服务端响应方法
		/// </summary>
		/// <param name="output">输出数据</param>
		/// <returns>服务端应响应的状态码</returns>
		public delegate ushort ServerResponseMethod(Hashtable output);

		/// <summary>
		/// 跨域检查
		/// </summary>
		/// <param name="origin">来源网址</param>
		/// <returns>是否允许跨域请求</returns>
		public static bool CrossDomainCheck(HttpRequest request)
		{
			string origin = request.Headers["Origin"];
			if (String.IsNullOrEmpty(origin)) return false;
			return true;
		}

		public ServerManage(IServerManager server)
		{
			this.server = server;
			this.files = new ServerFileManage(server);
			this.methods = new Dictionary<ushort, ServerResponseMethod>();
			this.CODE = ServerCode.CreateFromConfigure("ServerCodeFile", this.server);
			this.CONFIGURE = ServerConfigure.CreateFromConfigure("ConfigureFile", this.server);

			this.InitMethod();
		}
		
		/// <summary>
		/// CMD处理方法集
		/// </summary>
		private readonly Dictionary<ushort, ServerResponseMethod> methods;

		private readonly ServerFileManage files;

		/// <summary>
		/// 当前请求上下文
		/// </summary>
		private readonly IServerManager server;

		/// <summary>
		/// 服务端响应代码表
		/// </summary>
		private readonly ServerCode CODE;

		/// <summary>
		/// 服务端配置表
		/// </summary>
		private readonly ServerConfigure CONFIGURE;

		/// <summary>
		/// 是否需要请求方进行身份验证
		/// </summary>
		private bool AuthRequire
		{
			get
			{
				short auth;
				if (!CONFIGURE.Configure.TryParse<short>("AUTH", out auth))
				{
					auth = 1;
				}
				return auth > 0;
			}
		}

		/// <summary>
		/// 是否需要请求方提供内容HASH
		/// </summary>
		private bool HashRequire
		{
			get
			{
				short hash;
				if (!CONFIGURE.Configure.TryParse<short>("HASH", out hash))
				{
					hash = 1;
				}
				return hash > 0;
			}
		}

		

		/// <summary>
		/// 注册CMD请求处理方法
		/// </summary>
		/// <param name="name">CMD名称</param>
		/// <param name="method">处理方法</param>
		public void RegistMethod(string name, ServerResponseMethod method)
		{
			methods[CONFIGURE.Commands[name]] = method;
		}
		
		/// <summary>
		/// 获取当前请求的CMD值
		/// </summary>
		/// <returns></returns>
		private ushort GetRequestCommand()
		{
			ushort cmd;
			var key = CONFIGURE.Headers["NAME"];
			string _cmd = server.RequestHeader(key);
			if (String.IsNullOrEmpty(_cmd)) return 0;
			
			if(!ushort.TryParse(_cmd, out cmd))
			{
				cmd = CONFIGURE.Commands["UNKN"];
			}
			return cmd;
		}

		/// <summary>
		/// 验证当前请求
		/// </summary>
		/// <returns></returns>
		private bool AuthenRequest()
		{
			return true;
		}

		#region CMD通用处理方法

		private static ushort UNKN(Hashtable output)
		{
			return 10000;
		}
		private ushort AUTH(Hashtable output)
		{
			if (AuthenRequest())
			{
				return 200;
			} else
			{
				return 10001;
			}
		}

		#endregion

		/// <summary>
		/// 调用CMD方法
		/// </summary>
		/// <param name="cmd">CMD标值</param>
		/// <param name="output">输出数据</param>
		private void InvokeCommand(ushort cmd, Hashtable output)
		{
			ServerResponseMethod method;
			if (methods.ContainsKey(cmd))
			{
				method = methods[cmd];
			}
			else
			{
				method = UNKN;
			}
			var code = method(output);
			var codeItem = CODE.GetItem(code);
			if(codeItem.code != 200
				&& codeItem.code != 0
				)
			{
				output.Clear();
			}
			output["status"] = codeItem.code;
			output["message"] = codeItem.message;
		}

		/// <summary>
		/// 输出
		/// </summary>
		public void Output()
		{
			var cmd = GetRequestCommand();
			var output = new Hashtable();
			InvokeCommand(cmd, output);
			var json = Newtonsoft.Json.JsonConvert.SerializeObject(output);
			server.Response(json);
			server.ResponseEnd();
		}

		/// <summary>
		/// 初始化CMD方法
		/// </summary>
		private void InitMethod()
		{
			RegistMethod("NONE", delegate (Hashtable output) {
				output["header"] = CONFIGURE.Headers["NAME"];
				output["next"] = CONFIGURE.Commands["HELP"];
				return 0;
			});

			RegistMethod("UNKN", UNKN);

			RegistMethod("HELP", delegate (Hashtable output) {
				output["commands"] = CONFIGURE.Commands;
				return 200;
			});
			RegistMethod("HELO", delegate (Hashtable output) {
				output["auth"] = AuthRequire;
				output["hash"] = HashRequire;
				if (AuthRequire)
				{
					output["next"] = CONFIGURE.Commands["AUTH"];
				}
				else
				{
					output["next"] = CONFIGURE.Commands["CONF"];
				}
				return 200;
			});
			RegistMethod("AUTH", AUTH);

			RegistMethod("CONF", delegate (Hashtable output) {

				if (!AuthenRequest()) return 10001;

				output["configure"] = CONFIGURE.Configure;
				output["headers"] = CONFIGURE.Headers;
				output["next"] = CONFIGURE.Commands["SHND"];
				return 200;
			});

			RegistMethod("SHND", delegate (Hashtable output) {

				if (!AuthenRequest()) return 10001;

				IServerFile file;

				try
				{
					file = files.CreateFile(server.RequestStream());
				}
				catch
				{
					return 11000;
				}
				if (file.ClientFileInfo.IsEmpty)
				{
					return 11001;
				}
				if (HashRequire && String.IsNullOrEmpty(file.ClientFileInfo.hash))
				{
					return 11002;
				}

				if (!file.SaveInfo())
				{
					return 11003;
				}

				output["guid"] = file.Id;
				output["next"] = CONFIGURE.Commands["UPLO"];
				return 200;
			});

			RegistMethod("UPLO", delegate (Hashtable output) {

				if (!AuthenRequest()) return 10001;

				var key = CONFIGURE.Headers["GUID"] ?? "GUID";
				var guid = server.RequestHeader(key);
				if (String.IsNullOrEmpty(guid))
				{
					return 12001;
				}
				var file = files.GetServerFile(guid);
				if (file == null)
				{
					return 12002;
				}
				if(!file.SaveTemp(server.RequestStream()))
				{
					return 12003;
				}
				
				return 200;
			});

			RegistMethod("DONE", delegate (Hashtable output) {

				if (!AuthenRequest()) return 10001;

				var key = CONFIGURE.Headers["GUID"] ?? "GUID";
				var guid = server.RequestHeader(key);
				if (String.IsNullOrEmpty(guid))
				{
					return 12001;
				}
				var file = files.GetServerFile(guid);
				if (file == null)
				{
					return 12002;
				}

				if (HashRequire && !file.CheckHash(CONFIGURE.Configure["HASHTYPE"].ToString()))
				{
					return 13001;
				}

				if(!file.Finalize())
				{
					return 13002;

				}

				return 200;
			});

		}

		//////////////////
	}
}